package com.bt7274.controller;

import com.bt7274.base.BaseInfoProperties;
import com.bt7274.base.TorrentParser;
import com.bt7274.config.SiteBasicConfig;
import com.bt7274.config.TrackerConfig;
import com.bt7274.enums.SortType;
import com.bt7274.exception.MyCustomException;
import com.bt7274.grace.result.GraceJSONResult;
import com.bt7274.grace.result.ResponseStatusEnum;
import com.bt7274.pojo.*;
import com.bt7274.pojo.bo.TorrentUploadBO;
import com.bt7274.pojo.vo.TorrentDetailVO;
import com.bt7274.pojo.vo.TorrentListVO;
import com.bt7274.pojo.vo.UserInfoVO;
import com.bt7274.service.*;
import com.bt7274.utils.PagedGridResult;
import com.github.pagehelper.PageHelper;
import com.bt7274.utils.SessionUtils;
import com.bt7274.utils.URLEncodeUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.mahout.cf.taste.common.TasteException;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.owasp.html.PolicyFactory;

/**
 * @Data 2025/6/7 23:03
 * @Author hp
 * @Version 1.0
 * @Description TorrentController
 */

@Slf4j
@RestController    //表示可以被前端调用
@RequestMapping("torrent")
@Tag(name = "种子管理", description = "种子上传，下载")
public class TorrentController extends BaseInfoProperties {

    @Resource
    private CategoryService categoryService;

    @Resource
    private UserService userService;

    @Resource
    private UserExpService userExpService;

    @Resource
    private PolicyFactory safeHTMLPolicy;

    @Resource
    private PromotionPolicyService promotionPolicyService;

    @Resource
    private SettingService settingService;

    @Resource
    private TorrentService torrentService;

    @Resource
    @Qualifier("torrentsDirectory")
    private File torrentsDirectory;

    @Resource
    private HttpServletRequest request;

    @Resource
    private TorrentCommentService torrentCommentService;

    @Resource
    private UserTorrentLikeService userTorrentLikeService;

    @Resource
    private UserTorrentFavoriteService userTorrentFavoriteService;

    /**
     * @Description 新增或更新种子
     * @Author hp
     * @param torrentUploadBO
     * @return GraceJSONResult
     */
    @PostMapping("/upload")
    public GraceJSONResult upload(@Valid TorrentUploadBO torrentUploadBO) throws Exception{
        // 防止跨站脚本（XSS）攻击
        torrentUploadBO.setDescription(safeHTMLPolicy.sanitize(torrentUploadBO.getDescription()));
        String userId = SessionUtils.getCurrentUserId();
        if (userId == null) {
            throw new MyCustomException(ResponseStatusEnum.UN_LOGIN);
        }
        UserInfoVO userInfo = userService.getUserInfo(userId);
        Category category = categoryService.getCategoryBySlug(torrentUploadBO.getCategory());
        PromotionPolicy promotionPolicy = promotionPolicyService.getDefaultPromotionPolicy();
        SiteBasicConfig siteBasicConfig = settingService.get(SiteBasicConfig.getConfigKey(), SiteBasicConfig.class);
        if (category == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.CATEGORY_ERROR);
        }
        // TODO 匿名目前没用
        String publisher = userInfo.getUsername();
        String publisherUrl = siteBasicConfig.getSiteBaseURL() + "/user/" + userInfo.getId();
        if (torrentUploadBO.isAnonymous()) {
            publisher = "Anonymous";
            publisherUrl = siteBasicConfig.getSiteBaseURL();
        }
        System.out.println(torrentUploadBO.getTag());
        List<String> tagList = torrentUploadBO.getTag() != null ? torrentUploadBO.getTag() : new ArrayList<>();
        // 将 tagList 拼接成以逗号分隔的字符串
        String tagString = String.join(",", tagList);
        try {
            TorrentParser parser = new TorrentParser(torrentUploadBO.getFile().getBytes(), true);
            parser.rewriteForTracker(siteBasicConfig.getSiteName(), publisher, publisherUrl);
            String infoHash = parser.getInfoHash();
            if (torrentService.getTorrentByHash(infoHash) != null) {
                return GraceJSONResult.errorCustom(ResponseStatusEnum.TORRENT_ALREADY_EXIST_ERROR);
            }
            // 保存种子文件到本地
            Files.write(new File(torrentsDirectory, infoHash + ".torrent").toPath(), parser.save());
            // 保存种子信息到数据库
            Torrent torrent = new Torrent();
            torrent.setInfoHash(infoHash);
            torrent.setUserId(userId);
            torrent.setTitle(torrentUploadBO.getTitle());
            torrent.setSize(parser.getTorrentFilesSize());
            torrent.setCategoryId(category.getId());
            torrent.setTags(tagString);
            torrent.setDescription(torrentUploadBO.getDescription());
            torrent.setCreateTime(LocalDateTime.now());
            torrent.setUpdatedTime(LocalDateTime.now());
            //torrent.setUnderReview(StpUtil.hasPermission("torrent:bypass_review"));
            torrent.setUnderReview(false);
            torrent.setPromotionPolicyId(promotionPolicy.getId());
            torrent.setAnonymous(torrentUploadBO.isAnonymous());
            torrentService.createOrUpdate(torrent);
            userExpService.rewardForSeed(userId, torrent.getSize());
            return GraceJSONResult.ok("种子上传成功！");
        } catch (MyCustomException e) {
            return GraceJSONResult.errorCustom(e.getResponseStatusEnum());
        }
    }

    /**
     * @Description 下载种子
     * @Author hp
     * @param torrentId
     * @return HttpEntity<?>
     */
    @GetMapping("/{torrentId}/download")
    public HttpEntity<?> download(@PathVariable("torrentId") Long torrentId) throws Exception {
        String userId = SessionUtils.getCurrentUserId();
        if (userId == null) {
            throw new MyCustomException(ResponseStatusEnum.UN_LOGIN);
        }
        User user = userService.getUserById(userId);
        TrackerConfig trackerConfig = settingService.get(TrackerConfig.getConfigKey(), TrackerConfig.class);
        Torrent torrent = torrentService.getTorrentById(torrentId);
        if (torrent == null) {
            throw new MyCustomException(ResponseStatusEnum.TORRENT_NOT_EXIST_ERROR);
        }
        if (!torrent.getUnderReview()) {
            throw new MyCustomException(ResponseStatusEnum.TORRENT_NOT_REVIEW_ERROR);
        }
        UserExp userExp = userExpService.getUserExp(userId);
        // 检查用户是否有足够的bt
        double bt = promotionPolicyService.getPromotionPolicyById(torrent.getPromotionPolicyId()).getBtRatio() * userExpService.calculateDownloadCost(torrent.getSize());
        if (userExp.getBt() < bt) {
            throw new MyCustomException(ResponseStatusEnum.BT_NOT_ENOUGH_ERROR);
        }
        // 花费下载用户bt，做种用户不花费bt
        if (!userId.equals(torrent.getUserId())) {
            userExpService.updateBt(userId, (int) -bt);
        }
        File torrentFile = new File(torrentsDirectory, torrent.getInfoHash() + ".torrent");
        if (!torrentFile.exists()) {
            throw new MyCustomException(ResponseStatusEnum.TORRENT_LOST_ERROR);
        }
        TorrentParser parser = new TorrentParser(Files.readAllBytes(torrentFile.toPath()), false);
        parser.rewriteForUser(trackerConfig.getTrackerURL(), userService.getPasskeyById(torrent.getUserId()), user);

        String fileName = "[" + trackerConfig.getTorrentPrefix() + "] " + torrent.getTitle() + ".torrent";
        HttpHeaders header = new HttpHeaders();
        header.set(HttpHeaders.CONTENT_TYPE, "application/x-bittorrent");
        header.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncodeUtil.urlEncode(fileName, false));
        return new HttpEntity<>(parser.save(), header);
    }

    /**
     * @Description 获取种子详情
     * @Author hp
     * @param torrentId
     * @return GraceJSONResult
     */
    @GetMapping("/{torrentId}")
    public GraceJSONResult getTorrentDetail(@PathVariable("torrentId") Long torrentId) {
        String userId = SessionUtils.getCurrentUserId();
        if (userId == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.UN_LOGIN);
        }
        TorrentDetailVO torrentDetail = torrentService.getTorrentDetail(torrentId, userId);
        if (torrentDetail != null) {
            SiteBasicConfig siteBasicConfig = settingService.get(SiteBasicConfig.getConfigKey(), SiteBasicConfig.class);
            String siteBaseURL = siteBasicConfig.getSiteBaseURL();
            String downloadLink = String.format("%s/torrent/%d/download", siteBaseURL, torrentDetail.getTorrentId());
            torrentDetail.setDownloadLink(downloadLink);
            return GraceJSONResult.ok(torrentDetail);
        } else {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.TORRENT_NOT_EXIST_ERROR);
        }
    }

    /**
     * @Description 获取审核通过的种子列表
     * @Author hp
     * @param
 * @return GraceJSONResult
     */
    @GetMapping("/list")
    public GraceJSONResult list(@RequestParam(defaultValue = "1") int page,
                                @RequestParam(defaultValue = "8") int pageSize) {
        String userId = SessionUtils.getCurrentUserId();
        if (userId == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.UN_LOGIN);
        }
        return GraceJSONResult.ok(torrentService.queryAllReviewed(page, pageSize));
    }

    /**
     * 审核种子
     * @param infoHash 种子的 infoHash
     * @param isApproved 审核结果，0 表示不通过，1 表示通过
     * @return 审核操作结果
     */
    @PostMapping("/review/{info_hash}")
    public GraceJSONResult reviewTorrent(@PathVariable("info_hash") String infoHash, @RequestParam int isApproved) {
        if (isApproved != 0 && isApproved != 1) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.USER_PARAMS_ERROR);
        }
        try {
            // 调用服务层方法进行审核操作
            boolean result = torrentService.reviewTorrent(infoHash, isApproved);
            if (result) {
                return GraceJSONResult.ok("种子状态更新成功");
            } else {
                return GraceJSONResult.errorCustom(ResponseStatusEnum.TORRENT_NOT_EXIST_ERROR);
            }
        } catch (Exception e) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.SYSTEM_ERROR);
        }
    }

    @GetMapping("/review/list")
    public GraceJSONResult reviewList(@RequestParam(defaultValue = "1") int page,
                                      @RequestParam(defaultValue = "8") int pageSize) {
        String userId = SessionUtils.getCurrentUserId();
        if (userId == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.UN_LOGIN);
        }
        return GraceJSONResult.ok(torrentService.queryAllUnreviewed(page, pageSize));
    }


    @NotNull
    private GraceJSONResult listPage(int page, int pageSize, List<Torrent> torrents) {
        PageHelper.startPage(page, pageSize);
        PagedGridResult result = setterPagedGrid(torrents, page);
        return GraceJSONResult.ok(result);
    }
    /**
     * 根据类别 slug 获取对应类别的种子列表
     * @param slug 种子类别名称
     * @return 对应类别的种子列表
     */
    @GetMapping("/{slug}/list")
    public GraceJSONResult getTorrentsByCategory(@PathVariable String slug,
                                                 @RequestParam(defaultValue = "1") int page,
                                                 @RequestParam(defaultValue = "8") int pageSize) {
        String userId = SessionUtils.getCurrentUserId();
        if (userId == null) {
            throw new MyCustomException(ResponseStatusEnum.UN_LOGIN);
        }
        Category category = categoryService.getCategoryBySlug(slug);
        if (category == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.CATEGORY_ERROR);
        }
        PagedGridResult result = torrentService.getTorrentsByCategoryId(category.getId(), page, pageSize);
        result.setRows(addDownLink((List<TorrentListVO>) result.getRows()));
        return GraceJSONResult.ok(result);
    }

    /**
     * 模糊搜索种子，根据用户传入的字符串在 tags 和 description 字段进行匹配
     * @param keyword 用户输入的搜索关键词
     * @return 包含匹配种子列表的 JSON 结果
     */
    @GetMapping("/search")
    public GraceJSONResult searchTorrents(@RequestParam String keyword,
                                          @RequestParam(defaultValue = "relevance") String sortBy,
                                          @RequestParam(defaultValue = "1") int page,
                                          @RequestParam(defaultValue = "8") int pageSize) {
        String userId = SessionUtils.getCurrentUserId();
        if (userId == null) {
            throw new MyCustomException(ResponseStatusEnum.UN_LOGIN);
        }
        if (StringUtils.isBlank(keyword)) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.USER_PARAMS_ERROR);
        }
        SortType sortType = SortType.fromValue(sortBy);
        PagedGridResult result = torrentService.searchTorrents(keyword, sortType, page, pageSize);
        result.setRows(addDownLink((List<TorrentListVO>) result.getRows()));
        return GraceJSONResult.ok(result);
    }

    //获取当前用户上传的种子列表
    @GetMapping("/upload/list")
    public GraceJSONResult getUploadedTorrents(@RequestParam(defaultValue = "1") int page,
                                               @RequestParam(defaultValue = "8") int pageSize) {
        String userId = SessionUtils.getCurrentUserId();
        if (userId == null) {
            throw new MyCustomException(ResponseStatusEnum.UN_LOGIN);
        }
        PagedGridResult result = torrentService.getUploadedTorrentsByUserId(userId, page, pageSize);
        return GraceJSONResult.ok(result);
    }

    private List<TorrentListVO> addDownLink(List<TorrentListVO> voList) {
        SiteBasicConfig siteBasicConfig = settingService.get(SiteBasicConfig.getConfigKey(), SiteBasicConfig.class);
        for (TorrentListVO vo : voList) {
            // 拼接下载链接
            String siteBaseURL = siteBasicConfig.getSiteBaseURL();
            String downloadLink = String.format("%s/torrent/%d/download", siteBaseURL, vo.getId());
            vo.setDownloadLink(downloadLink);
        }
        return voList;
    }

    /**
     * 获取所有促销规则
     * @return 包含所有促销规则的 JSON 结果
     */
    @GetMapping("/promotionPolicy")
    public GraceJSONResult getPromotionPolicies() {
        try {
            List<PromotionPolicy> promotionPolicies = promotionPolicyService.queryAll();
            return GraceJSONResult.ok(promotionPolicies);
        } catch (Exception e) {
            log.error("获取促销规则失败: {}", e.getMessage(), e);
            return GraceJSONResult.errorMsg("获取促销规则失败");
        }
    }

    /**
     * 修改种子促销策略
     * @param torrentId 种子 ID
     * @param promotionPolicySlug 促销策略 Slug
     * @return 修改操作结果
     */
    @PutMapping("/{torrentId}/promotion/update")
    public GraceJSONResult updateTorrentPromotionPolicy(@PathVariable Long torrentId, @RequestParam String promotionPolicySlug) {
        String userId = SessionUtils.getCurrentUserId();
        if (userId == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.UN_LOGIN);
        }

        try {
            boolean result = torrentService.updateTorrentPromotionPolicy(torrentId, promotionPolicySlug);
            if (result) {
                return GraceJSONResult.ok("种子促销策略修改成功");
            } else {
                return GraceJSONResult.fail("种子促销策略修改失败");
            }
        } catch (MyCustomException e) {
            return GraceJSONResult.errorCustom(e.getResponseStatusEnum());
        } catch (Exception e) {
            log.error("修改种子促销策略时发生异常", e);
            return GraceJSONResult.errorCustom(ResponseStatusEnum.SYSTEM_ERROR);
        }
    }

    /**
     * 使用 Mahout 根据用户行为推荐种子
     * @return 推荐的种子列表
     */
    @GetMapping("/recommend")
    @Operation(summary = "使用 Mahout 根据用户行为推荐种子", description = "依据用户点赞、收藏、做种行为推荐种子")
    public GraceJSONResult recommendTorrentsByMahout() {
        String userId = SessionUtils.getCurrentUserId();
        if (userId == null) {
            return GraceJSONResult.errorCustom(ResponseStatusEnum.UN_LOGIN);
        }
        try {
            List<TorrentListVO> recommendedTorrents = torrentService.recommendTorrentsByMahout(userId);
            return GraceJSONResult.ok(addDownLink(recommendedTorrents));
        } catch (TasteException e) {
            return GraceJSONResult.errorMsg("推荐失败: " + e.getMessage());
        }
    }
    //删除种子
    @GetMapping("/{torrentId}/delete")
    public GraceJSONResult deleteTorrent(@PathVariable Long torrentId) {
        boolean deleted = torrentService.deleteTorrentById(torrentId);
        if (deleted) {
            return GraceJSONResult.ok("种子删除成功");
        } else {
            return GraceJSONResult.errorMsg("种子删除失败或不存在");
        }
    }
}
